#ifndef __C_MM_VMA_H__
#define __C_MM_VMA_H__

#include "std/list.h"
#include "std/avltree.h"
#include "arch/traps.h"
#include "kernel/spinlock.h"

#ifdef __cplusplus
extern "C" {
#endif

// common flags
#define VMA_FLAG_NO_RELEASE				(1)
#define VMA_FLAG_WRITE					(2)
#define VMA_FLAG_EXECUTE				(4)
#define VMA_FLAG_SWAPABLE				(8)
#define VMA_FLAG_KRNL_SPACE				(16)
#define VMA_FLAG_PRECOMMITTED			(32)

// flags for heap
#define VMA_FLAG_HEAP					(64)
#define VMA_FLAG_HEAP_USE_LOW_MEM		(128)
#define VMA_FLAG_HEAP_LAZY_RELEASE		(256)

// flags for file mapping
#define VMA_FLAG_FILE_MAPPING			(512)

struct vma;

typedef struct vma_mgr
{
	listnode_t	vma_list_head;
	avl_node_t*	vma_avl_head;

	struct vma*  latest_vma;			// cache of latest vma pointer
	unsigned int vma_count;			// count fo vma in this manager
	unsigned int addr;				// start address
	unsigned int pages;				// how many pages covered by this mgr

	spinlock_t	 spinlock;
}
vma_mgr_t;

#define VMA_MGR_QUICK_SEARCH_CNT	(32)

struct vma;
struct vma_operations
{
	void* (*vmalloc)(struct vma*, size_t pages);
	int (*vfree)(struct vma*, void* addr, size_t pages);
	int (*handle_page_fault)(regs_t*, struct vma*, uint);
};

// all kernel heap vma could use this
extern struct vma_operations kernel_heap_ops;

typedef struct vma
{
	listnode_t ownerlist;
	avl_node_t avlnode;
	
	unsigned int addr;
	unsigned int size;				// size is in page count
	unsigned int flags;
	unsigned int userdata;			// this is reserved for customer use

	struct vma_operations* vma_ops;
}
vma_t;

#define vma_spinlock(pvma)		spin_lock(&((pvma)->spinlock))
#define vma_spinunlock(pvma)	spin_unlock(&((pvma)->spinlock))

// this indicate the kernel vma manager
// in the system
extern vma_mgr_t kernel_vma_mgr;

extern vma_t krnl_low_premapped_area1;
extern vma_t kernel_area;
extern vma_t page_entry_table_area;
extern vma_t krnl_proc_preoccupied_area;
extern vma_t krnl_low_premapped_area2;
extern vma_t default_kernel_heap;

static bool init_vma_mgr(vma_mgr_t* vmamgr, uint addr, uint pages)
{
	kassert(NULL != vmamgr);
	if (!pages || addr - 1 > 0xFFFFFFFF - pages * PAGE_SZ)
		return false;

	memset(vmamgr, 0, sizeof(vma_mgr_t));
	init_spinlock(&(vmamgr->spinlock));

	listnode_init(vmamgr->vma_list_head);
	vmamgr->addr = addr;
	vmamgr->pages = pages;
	return true;
}

static void init_vma(vma_t* vma)
{
	kassert(NULL != vma);
	memset(vma, 0, sizeof(vma_t));
	listnode_init(vma->ownerlist);
	vma->flags |= VMA_FLAG_NO_RELEASE;
}

// exposed functions

// create a vma with specific start address
vma_t* create_vma(vma_mgr_t* vmamgr, uint addr, uint pages);

// create a vma manager
vma_mgr_t* create_vma_manager(void);

// insert a vma to specific manager
int insert_vma(vma_mgr_t* vmamgr, vma_t* vma);

// find a vma by the address
vma_t* find_vma_by_addr(vma_mgr_t* vmamgr, uint addr);

// get first vma
vma_t* vma_first(vma_mgr_t *vma_mgr);

// get next vma
vma_t* vma_next(vma_mgr_t* vma_mgr, vma_t* prev);

// request for allocation of an vma
int request_vma(vma_mgr_t* vmamgr, vma_t *vma, uint _align);

// global initialization
int global_init_vma_module(void);

#ifdef __cplusplus
}
#endif
#endif
/* EOF */
